using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text;
using System.Xml;
using System.Xml.Serialization;

namespace Common.Utilities.Dispatcher
{
    /// <summary>
    /// 
    /// </summary>
    [Serializable]
    public abstract class ResultBase:ICloneable
    {
        private Guid _OwnerWorkerInstanceID;
        /// <summary>
        /// 
        /// </summary>
        public Guid OwnerWorkerInstanceID
        {
            get { return _OwnerWorkerInstanceID; }
            set { _OwnerWorkerInstanceID = value; }
        }

        private Guid _OwnerComponentID;
        /// <summary>
        /// 
        /// </summary>
        public Guid OwnerComponentID
        {
            get { return _OwnerComponentID; }
            set { _OwnerComponentID = value; }
        }

        private DateTime _FinishTime;
        /// <summary>
        /// 
        /// </summary>
        public DateTime FinishTime
        {
            get { return _FinishTime; }
            set { _FinishTime = value; }
        }

        #region ctor
        /// <summary>
        /// 
        /// </summary>
        public ResultBase():this(Guid.Empty,Guid.Empty)
        {
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="componentID"></param>
        /// <param name="jobTicketID"></param>
        public ResultBase(Guid componentID, Guid jobTicketID)
        {
            this.OwnerWorkerInstanceID = jobTicketID;
            this.OwnerComponentID = componentID;
            this._FinishTime = DateTime.Now;
        }
        #endregion

        /// <summary>
        /// 
        /// </summary>
        /// <param name="xDoc"></param>
        /// <param name="parentNode"></param>
        /// <returns></returns>
        public virtual XmlNode Serialize(ref XmlDocument xDoc, ref XmlNode parentNode)
        {
            XmlNode xNode = XmlDataUtil.AddElement(ref xDoc, parentNode, "Output");
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "Type", this.GetType().FullName);
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "WorkerID", this.OwnerWorkerInstanceID.ToString());
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "FinishTime", this.FinishTime.ToString());

            XmlSerializer xmlSerializer = new XmlSerializer(this.GetType());
            MemoryStream ms = new MemoryStream();
            XmlTextWriter writer = new XmlTextWriter(ms, Encoding.Default);
            xmlSerializer.Serialize(writer, this);
            writer.Flush();
            writer.Close();
            byte[] buffer = new byte[ms.Length];
            ms.Read(buffer, 0, (int)ms.Length);
            ms.Close();
            string data = Encoding.Default.GetString(buffer);
            XmlDataUtil.AddCdataElement(ref xDoc, xNode, "Data", data);
            return xNode;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="xNode"></param>
        /// <returns></returns>
        public virtual ResultBase Instantiate(XmlNode xNode)
        {
            if (xNode.Attributes.GetNamedItem("Type").Value == this.GetType().FullName)
            {
                string data = XmlDataUtil.GetCdataValue(xNode, "");
                XmlSerializer xmlSerializer = new XmlSerializer(this.GetType());
                XmlReader reader = new XmlTextReader(new StringReader(data));
                return (ResultBase)xmlSerializer.Deserialize(reader);
            }
            else
            {
                throw new Exception("Unable to instantiate output: invalid type specified in xml node");
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="excludeMemberFromTypes"></param>
        /// <returns></returns>
        public virtual Dictionary<string, Type> GetBindableMemberNames(Type[] excludeMemberFromTypes)
        {
            Dictionary<string,Type> bindableMemberNames=new Dictionary<string, Type>();
            FieldInfo[] fields = this.GetType().GetFields();
            if(fields !=null && fields.Length>0)
            {
                foreach(FieldInfo field in fields)
                {
                    bindableMemberNames.Add(field.Name,field.FieldType);
                }
            }
            PropertyInfo[] props = this.GetType().GetProperties();
            if(props !=null && props.Length>0)
            {
                foreach(PropertyInfo prop in props)
                {
                    bindableMemberNames.Add(prop.Name,prop.PropertyType);
                }
            }

            if(excludeMemberFromTypes !=null && excludeMemberFromTypes.Length>0)
            {
                foreach(Type excludedType in excludeMemberFromTypes)
                {
                    FieldInfo[] exFields = excludedType.GetFields();
                    if (exFields != null && exFields.Length > 0)
                    {
                        foreach (FieldInfo field in exFields)
                        {
                            if (bindableMemberNames.ContainsKey(field.Name))
                                bindableMemberNames.Remove(field.Name);
                        }
                    }
                    PropertyInfo[] exProps = excludedType.GetProperties();
                    if (exProps != null && exProps.Length > 0)
                    {
                        foreach (PropertyInfo prop in exProps)
                        {
                            if (bindableMemberNames.ContainsKey(prop.Name))
                                bindableMemberNames.Remove(prop.Name);
                        }
                    }
                }
            }

            return bindableMemberNames;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="memberName"></param>
        /// <returns></returns>
        public virtual object ReadMemberValue(string memberName)
        {
            FieldInfo fieldInfo = this.GetType().GetField(memberName);
            if(fieldInfo !=null)
            {
                return fieldInfo.GetValue(this);
            }
            else
            {
                PropertyInfo propInfo = this.GetType().GetProperty(memberName);
                if(propInfo !=null)
                {
                    return propInfo.GetValue(this, null);
                }
            }
            return null;
        }

        #region Implementation of ICloneable

        /// <summary>
        /// Creates a new object that is a copy of the current instance.
        /// </summary>
        /// <returns>
        /// A new object that is a copy of this instance.
        /// </returns>
        /// <filterpriority>2</filterpriority>
        public object Clone()
        {
            ResultBase newOutput = (ResultBase)Activator.CreateInstance(this.GetType());
            try
            {
                PropertyInfo[] props = this.GetType().GetProperties();
                foreach (PropertyInfo prop in props)
                {
                    if (prop.CanRead && prop.CanWrite)
                    {
                        object propValue = prop.GetValue(this, null);
                        prop.SetValue(newOutput, propValue, null);
                    }
                }
            }
            catch (Exception ex)
            {
                ExceptionHandler.HandlesException(ex);
            }
            return newOutput;
        }

        #endregion
    }
}
